#338 Globalize3
- Download:
- source codeProject Files in Zip (93.4 KB)
- mp4Full Size H.264 Video (16.4 MB)
- m4vSmaller H.264 Video (7.84 MB)
- webmFull Size VP8 Video (8.46 MB)
- ogvFull Size Theora Video (17.9 MB)
여러 개의 Article
레코드를 보여주는 블로그 어플리케이션 중의 한 페이지를 아래에서 볼 수 있습니다. 이 어플리케이션은 다국어를 지원하기 때문에 페이지 상단에 있는 링크를 통해서 보여지게 될 언어를 변경할 수 있게 해 줍니다.
“Wookieespeak” 링크를 클릭할 때 페이지 상단에 있는 타이틀은 변경되지만 각 article의 내용은 여전히 영어로 표시됩니다. 이런 경우, 어떻게 하면 데이터베이스로부터 가져오는 내용을 변환해서 사용자가 선호하는 언어로 보이게 할 수 있을까요?
레일스 어플리케이션에서의 다국어 지원은 대개 YAML 파일을 이용해서 구현합니다. /config/locales 디렉토리에는 어플리케이션에서 지원하는 해당 언어별로 번역한 내용들이 텍스트 파일 형태로 존재하게 됩니다. 이러한 접근방법은 138번 연제에서 상세하게 다루었지만, 이렇게 하면 정적인 텍스트에 대해서는 잘 동작하는 반면, 데이터베이스로부터 가져오는 텍스트에 대해서는 다국어로 보여 줄 수 없다는 문제점이 있습니다. 문제 해결을 위해서는 데이터베이스에 테이블을 생성한 후에 각각의 번역내용을 저장할 필요가 있는데, Globalize3 젬을 이용하면 이러한 작업을 보다 쉽게 할 수 있습니다. Globalize3는 번역하고자하는 각 모델에 대해서 별도의 테이블을 생성해 주고 사용자가 선택한 로케일에 해당하는 언어로 번역할 수 있게 적절하게 변경해 줍니다. 본 어플리케이션에서 이 젬을 사용하기 위해서, Gemfile에 이 젬을 추가한 후에 bundle install
할 필요가 있습니다.
gem 'globalize3'
자 이제, 번역하고자하는 모델 클래스 파일로 가서 translates
메소드에 다국어로 볼 수 있도록 할 컬럼명을 명시해 줄 수 있습니다. 여기서는 Article
의 name과 content 컬럼을 사용합니다.
class Article < ActiveRecord::Base translates :name, :content end
우리는 여기서 번역한 내용을 저장할 데이터베이스 테이블을 생성할 필요가 있습니다. 이 젬의 README 파일을 보면 알 수 있듯이, 해당 모델에 대한 테이블을 생성하는 동일한 마이그레이션 내에서 create_translation_table!
메소드를 사용할 수 있지만 articles
테이블에 이미 데이터가 있기 때문에 우리는 다른 방법을 이용할 것입니다. 즉, 별도의 마이그레이션 파일을 만든 후에 기존의 데이터를 마이그레이션할 것입니다. 우선, 어플리케이션에서 create_article_translations
라는 이름의 마이그레이션 파일을 생성할 것입니다.
$ rails g migration create_article_translations
우리는 README 파일에서 알려주는 데로 이 마이그레이션 파일의 내용을 수정할 것입니다. 번역하고자 하는 모델에 대해서 create_translations_table!
메소드를 호출하고, 이 때 번역하고자하는 컬럼의 이름들을 넘겨 줍니다.
class CreateArticleTranslations < ActiveRecord::Migration def up Article.create_translation_table!({ name: :string, content: :text }, { migrate_data: true }) end def down Article.drop_translation_table! migrate_data: true end end
이 두 컬럼의 데이터는 이제 articles
테이블이 아니라 새로이 생성된 article_translations
테이블에 저장될 것입니다. 자 이제, 변경된 내용을 적용하기 위해서 데이터베이스를 마이그레이션할 필요가 있습니다.
$ rake db:migrate
레일스 콘솔을 이용하면 지금까지해 온 작업이 어떻게 동작하는지 확인할 수 있습니다. 현재 로케일을 확인해 보면 디폴트로 영어가 지정되어 있는 것을 알 수 있습니다. 첫번째 article의 이름을 불러올 경우 새로 만든 article_translations
테이블에서 그 값을 가져오게 될 것입니다.
1.9.3-p125 :001 > I18n.locale => :en 1.9.3-p125 :002 > Article.first.name Article Load (0.2ms) SELECT "articles".* FROM "articles" LIMIT 1 Article::Translation Load (0.2ms) SELECT "article_translations".* FROM "article_translations" WHERE "article_translations"."article_id" = 1 => "Superman"
이 때 Wookieespeak로 로케일을 변경하고 방금전의 첫번째 article의 이름을 불러 오게 되면, 아직 번역된 텍스트가 존재하지 않기 때문에, nil값을 얻게 될 것입니다.
1.9.3-p125 :003 > I18n.locale = :wk => :wk 1.9.3-p125 :004 > Article.first.name Article Load (0.3ms) SELECT "articles".* FROM "articles" LIMIT 1 Article::Translation Load (0.2ms) SELECT "article_translations".* FROM "article_translations" WHERE "article_translations"."article_id" = 1 Article::Translation Load (0.3ms) SELECT "article_translations".* FROM "article_translations" WHERE "article_translations"."article_id" = 1 AND "article_translations"."locale" = 'wk' LIMIT 1 => nil
Globalize3는 해당 컬럼에 대한 getter와 setter의 동작을 변경해서 현재의 언어로 번역할 수 있게 해 줍니다. 현재의 로케일이 wk
로 설정되어 있는 상태에서 첫번째 article의 이름을 변경하게 되면 해당 로케일로 article_translations
테이블에 하나의 레코드를 추가하게 되고 해당 컬럼명을 다시 불러올 때 우리는 방금전에 번역했던 값을 보게 될 것입니다.
1.9.3-p125 :005 > Article.first.update_attribute(:name, "Ahhyya") 1.9.3-p125 :006 > Article.first.name Article Load (0.3ms) SELECT "articles".* FROM "articles" LIMIT 1 Article::Translation Load (0.2ms) SELECT "article_translations".* FROM "article_translations" WHERE "article_translations"."article_id" = 1 => "Ahhyya"
다시 로케일을 영어로 변경하면 해당 article의 이름을 영어로 보게 될 것입니다.
1.9.3-p125 :007 > I18n.locale = :en => :en 1.9.3-p125 :008 > Article.first.name Article Load (0.2ms) SELECT "articles".* FROM "articles" LIMIT 1 Article::Translation Load (0.2ms) SELECT "article_translations".* FROM "article_translations" WHERE "article_translations"."article_id" = 1 => "Superman"
이제 이것이 웹사이트에서 제대로 동작한다는 것을 확인했습니다. 이제 변경된 내용들이 유지되는지 확인하기 위해 서버를 재시작할 필요가 있습니다. 그러나 이때도 해당 페이지의 영어 버전이 해당 데이터가 마이그레이션 된 것과 동일하게 보여야 합니다. 그러나 Wookieespeak로 해당 페이지를 볼 경우에 우리는 아무 것도 볼 수 없을 것입니다.
여기서 첫번째 article은 컨솔에서 설정했던 이름을 가지게 될 것이지만 우리가 번역할 수 있는 컬럼으로 지정했던 그외 다른 모든 속성들은 아직 아무런 값을 가지지 않게 될 것입니다. 그러나, 이들 article 중에 하나를 수정해서 번역한 내용으로 변경할 수 있습니다.
이 article은 이제 두개의 언어로 볼 수 있으며 이 두 언어를 교대로 바꿀 때 해당 언어로 번역된 내용을 볼 수 있을 것입니다. article의 edit 폼으로 접근할 때 텍스트 필드는 현재 선택된 언어에 대한 텍스트 내용을 보여 줄 것이고 언어 링크들을 클릭해서 언어들을 교대로 선택하면서 해당 언어로 article을 변경할 수 있을 것입니다. 만약 author name과 같이 번역 대상으로 지정하지 않은 속성에 대해서 번역작업을 하게 될 경우에는 article_translations
테이블이 아니라 articles
테이블에 그 번역된 내용이 저장될 것이기 때문에 모든 언어에 대해서 같은 내용으로 갱신 될 것입니다.
대체언어 사용하기
두개의 언어에 대해서 모든 데이터베이스 레코드를 번역한다는 것은 큰 일거리가 될 수 있습니다. 앞서 본 바와 같이, 현재 언어로 아직 번연되지 않은 속성에 대해서는 nil 값이 반환될 것입니다. 그러나 이때 우리는 대체할 수 있는 언어를 지정해 줄 수 있습니다. 이를 위해서, 어플리케이션의 config 파일을 수정해서 i18n.fallbacks
옵션을 추가해 주기만 하면 됩니다.
config.i18n.fallbacks = true
이렇게 해 주면, 현재 선택된 언어로 번역된 내용이 없을 경우에 디폴트 로케일(이경우에 영어)로 지정된 내용으로 대체될 것입니다. 변경된 내용이 제대로 적용되는지 확인하기 위해서 어플리케이션을 재시작할 필요가 있을 것입니다. 그러나 해당 페이지를 다시 로드할 때, Wookieespeak로 번역되지 않은 내용은 이제 영어로 대신 보이게 될 것입니다.