Add language target: simplified/traditional chinese

Ready for multilingualization. See /doc/EN/dev_guide/i18n_messages.md.
This commit is contained in:
Shunsuke Shibayama 2022-08-18 16:31:35 +09:00
parent a30be5d40d
commit 842fe10353
17 changed files with 459 additions and 183 deletions

View file

@ -29,6 +29,16 @@ japanese = [
"erg_parser/japanese",
"erg_compiler/japanese",
]
simplified_chinese = [
"erg_common/simplified_chinese",
"erg_parser/simplified_chinese",
"erg_compiler/simplified_chinese",
]
traditional_chinese = [
"erg_common/traditional_chinese",
"erg_parser/traditional_chinese",
"erg_compiler/traditional_chinese",
]
[dependencies]
erg_common = { version = "0.2.5", path = "./compiler/erg_common" }

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

View file

@ -14,6 +14,8 @@ homepage = "https://erg-lang.github.io/"
[features]
debug = []
japanese = []
simplified_chinese = []
traditional_chinese = []
[dependencies]
atty = "0.2.14"

View file

@ -58,8 +58,8 @@ impl DeserializeError {
0,
fn_name!(),
switch_lang!(
"the loaded .pyc file is broken",
"読み込んだ.pycファイルは破損しています"
"japanese" => "読み込んだ.pycファイルは破損しています",
"english" => "the loaded .pyc file is broken",
),
)
}
@ -69,14 +69,14 @@ impl DeserializeError {
0,
fn_name!(),
switch_lang!(
format!(
"japanese" => format!(
"{}型オブジェクトを予期しましたが、 読み込んだオブジェクトは{}型です",
expect, found
),
"english" => format!(
"expect a {} object, but the deserialized object is {}",
expect, found
),
format!(
"{}型オブジェクトを予期しましたが、 読み込んだオブジェクトは{}型です",
expect, found
)
),
)
}
@ -228,8 +228,8 @@ impl Deserializer {
0,
fn_name!(),
switch_lang!(
format!("cannot deserialize this object: {}", other),
format!("このオブジェクトは復元できません: {}", other)
"japanese" => format!("このオブジェクトは復元できません: {}", other),
"english" => format!("cannot deserialize this object: {}", other),
),
)),
}
@ -300,7 +300,10 @@ impl Deserializer {
return Err(DeserializeError::new(
0,
fn_name!(),
switch_lang!("failed to load bytes", "バイト列の読み込みに失敗しました"),
switch_lang!(
"japanese" => "バイト列の読み込みに失敗しました",
"english" => "failed to load bytes",
),
));
}
let len = Self::deserialize_u32(v);

View file

@ -321,10 +321,16 @@ impl ErrorCore {
}
pub fn bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self {
Self::new(errno, CompilerSystemError, loc, switch_lang!(
format!("this is a bug of Erg, please report it to https://github.com/...\ncaused from: {fn_name}:{line}"),
format!("これはErgのバグです、開発者に報告して下さい (https://github.com/...)\n{fn_name}:{line}より発生")
), None)
Self::new(
errno,
CompilerSystemError,
loc,
switch_lang!(
"japanese" => format!("これはErgのバグです、開発者に報告して下さい (https://github.com/...)\n{fn_name}:{line}より発生"),
"english" => format!("this is a bug of Erg, please report it to https://github.com/...\ncaused from: {fn_name}:{line}"),
),
None,
)
}
}

View file

@ -58,11 +58,17 @@ macro_rules! impl_display_for_enum_with_variant {
/// マクロはパラメータを展開しないので、format!のロスがなくなる
#[macro_export]
macro_rules! switch_lang {
($en: expr, $jp: expr $(,)*) => {{
if cfg!(feature = "japanese") {
$jp
(
$should_english: literal => $msg: expr,
) => {{ $msg }};
(
$lang_name: literal => $msg: expr,
$($rest_lang_name: literal => $rest_msg: expr,)+
) => {{
if cfg!(feature = $lang_name) {
$msg
} else {
$en
switch_lang!($($rest_lang_name => $rest_msg,)+)
}
}};
}

View file

@ -326,8 +326,8 @@ impl ValueObj {
panic!(
"{}",
switch_lang!(
format!("this object cannot be serialized: {other}"),
format!("このオブジェクトはシリアライズできません: {other}")
"japanese" => format!("このオブジェクトはシリアライズできません: {other}"),
"english" => format!("this object cannot be serialized: {other}"),
)
)
}

View file

@ -13,6 +13,8 @@ homepage = "https://erg-lang.github.io/"
# when "debug" feature is turned on, that of parser will also be turned on.
debug = [ "erg_common/debug", "erg_parser/debug" ]
japanese = [ "erg_common/japanese", "erg_parser/japanese" ]
simplified_chinese = [ "erg_common/simplified_chinese", "erg_parser/simplified_chinese" ]
traditional_chinese = [ "erg_common/traditional_chinese", "erg_parser/traditional_chinese" ]
[dependencies]
erg_common = { version = "0.2.5", path = "../erg_common" }

View file

@ -138,10 +138,20 @@ impl CompileError {
fn_name: &str,
line: u32,
) -> Self {
Self::new(ErrorCore::new(errno, CompilerSystemError, loc, switch_lang!(
format!("this is a bug of the Erg compiler, please report it to https://github.com/...\ncaused from: {fn_name}:{line}"),
format!("これはErg compilerのバグです、開発者に報告して下さい (https://github.com/...)\n{fn_name}:{line}より発生")
), None), input, "".into())
Self::new(
ErrorCore::new(
errno,
CompilerSystemError,
loc,
switch_lang!(
"japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい (https://github.com/...)\n{fn_name}:{line}より発生"),
"english" => format!("this is a bug of the Erg compiler, please report it to https://github.com/...\ncaused from: {fn_name}:{line}"),
),
None,
),
input,
"".into(),
)
}
pub fn stack_bug(
@ -151,14 +161,24 @@ impl CompileError {
block_id: usize,
fn_name: &str,
) -> Self {
Self::new(ErrorCore::new(0, CompilerSystemError, loc, switch_lang!(
format!("the number of elements in the stack is invalid (num of elems: {stack_len}, block id: {block_id})\n\
Self::new(
ErrorCore::new(
0,
CompilerSystemError,
loc,
switch_lang!(
"japanese" => format!("スタックの要素数が異常です (要素数: {stack_len}, ブロックID: {block_id})\n\
(https://github.com/...)\n\
{fn_name}"),
"english" => format!("the number of elements in the stack is invalid (num of elems: {stack_len}, block id: {block_id})\n\
this is a bug of the Erg compiler, please report it (https://github.com/...)\n\
caused from: {fn_name}"),
format!("スタックの要素数が異常です (要素数: {stack_len}, ブロックID: {block_id})\n\
(https://github.com/...)\n\
{fn_name}")
), None), input, "".into())
),
None,
),
input,
"".into(),
)
}
pub fn feature_error(input: Input, loc: Location, name: &str, caused_by: Str) -> Self {
@ -168,8 +188,9 @@ impl CompileError {
FeatureError,
loc,
switch_lang!(
format!("this feature({name}) is not implemented yet"),
format!("この機能({name})はまだ正式に提供されていません")
"japanese" => format!("この機能({name})はまだ正式に提供されていません"),
"simplified_chinese" => format!("该功能({name})还没有正式提供"),
"english" => format!("this feature({name}) is not implemented yet"),
),
None,
),
@ -195,10 +216,19 @@ impl TyCheckError {
}
pub fn checker_bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self {
Self::new(ErrorCore::new(errno, CompilerSystemError, loc, switch_lang!(
format!("this is a bug of the Erg compiler, please report it to https://github.com/...\ncaused from: {fn_name}:{line}"),
format!("これはErg compilerのバグです、開発者に報告して下さい (https://github.com/...)\n{fn_name}:{line}より発生")
), None), "".into())
Self::new(
ErrorCore::new(
errno,
CompilerSystemError,
loc,
switch_lang!(
"japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい (https://github.com/...)\n{fn_name}:{line}より発生"),
"english" => format!("this is a bug of the Erg compiler, please report it to https://github.com/...\ncaused from: {fn_name}:{line}"),
),
None,
),
"".into(),
)
}
pub fn feature_error(loc: Location, name: &str, caused_by: Str) -> Self {
@ -208,8 +238,8 @@ impl TyCheckError {
FeatureError,
loc,
switch_lang!(
format!("this feature({name}) is not implemented yet"),
format!("この機能({name})はまだ正式に提供されていません")
"japanese" => format!("この機能({name})はまだ正式に提供されていません"),
"english" => format!("this feature({name}) is not implemented yet"),
),
None,
),
@ -238,8 +268,8 @@ impl TyCheckError {
NameError,
loc,
switch_lang!(
format!("{name} is already declared"),
format!("{name}は既に宣言されています")
"japanese" => format!("{name}は既に宣言されています"),
"english" => format!("{name} is already declared"),
),
Option::<Str>::None,
),
@ -255,12 +285,18 @@ impl TyCheckError {
found_t: &Type,
) -> Self {
let name = readable_name(name);
Self::new(ErrorCore::new(0, TypeError, loc,
Self::new(
ErrorCore::new(
0,
TypeError,
loc,
switch_lang!(
format!("{name} was declared as {GREEN}{spec_t}{RESET}, but an {RED}{found_t}{RESET} object is assigned"),
format!("{name}{GREEN}{spec_t}{RESET}型として宣言されましたが、{RED}{found_t}{RESET}型のオブジェクトが代入されています")
), Option::<Str>::None),
caused_by
"japanese" => format!("{name}{GREEN}{spec_t}{RESET}型として宣言されましたが、{RED}{found_t}{RESET}型のオブジェクトが代入されています"),
"english" => format!("{name} was declared as {GREEN}{spec_t}{RESET}, but an {RED}{found_t}{RESET} object is assigned"),
),
Option::<Str>::None,
),
caused_by,
)
}
@ -272,8 +308,8 @@ impl TyCheckError {
TypeError,
loc,
switch_lang!(
format!("the type of {name} is not specified"),
format!("{name}の型が指定されていません")
"japanese" => format!("{name}の型が指定されていません"),
"english" => format!("the type of {name} is not specified"),
),
None,
),
@ -291,8 +327,8 @@ impl TyCheckError {
let hint = similar_name.map(|n| {
let n = readable_name(n);
switch_lang!(
format!("exists a similar name variable: {n}"),
format!("似た名前の変数があります: {n}")
"japanese" => format!("似た名前の変数があります: {n}"),
"english" => format!("exists a similar name variable: {n}"),
)
.into()
});
@ -302,8 +338,8 @@ impl TyCheckError {
NameError,
loc,
switch_lang!(
format!("{RED}{name}{RESET} is not defined"),
format!("{RED}{name}{RESET}という変数は定義されていません")
"japanese" => format!("{RED}{name}{RESET}という変数は定義されていません"),
"english" => format!("{RED}{name}{RESET} is not defined"),
),
hint,
),
@ -321,8 +357,8 @@ impl TyCheckError {
let hint = similar_name.map(|n| {
let n = readable_name(n);
switch_lang!(
format!("has a similar name attribute: {n}"),
format!("似た名前の属性があります: {n}")
"japanese" => format!("似た名前の属性があります: {n}"),
"english" => format!("has a similar name attribute: {n}"),
)
.into()
});
@ -332,8 +368,8 @@ impl TyCheckError {
AttributeError,
loc,
switch_lang!(
format!("{obj_t} object has no attribute {RED}{name}{RESET}"),
format!("{obj_t}型オブジェクトに{RED}{name}{RESET}という属性はありません")
"japanese" => format!("{obj_t}型オブジェクトに{RED}{name}{RESET}という属性はありません"),
"english" => format!("{obj_t} object has no attribute {RED}{name}{RESET}"),
),
hint,
),
@ -353,12 +389,12 @@ impl TyCheckError {
NotImplementedError,
callee.loc(),
switch_lang!(
format!(
"japanese" => format!(
"{callee}は{param_ts}を引数に取る呼び出し可能オブジェクトではありません"
),
"english" => format!(
"{callee} is not a Callable object that takes {param_ts} as an argument"
),
format!(
"{callee}は{param_ts}を引数に取る呼び出し可能オブジェクトではありません"
)
),
None,
),
@ -373,10 +409,19 @@ impl TyCheckError {
expect: &Type,
found: &Type,
) -> Self {
Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!(
format!("the type of {name} is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"),
format!("{name}の型が違います。\n予期した型: {GREEN}{expect}{RESET}\n与えられた型: {RED}{found}{RESET}")
), None), caused_by)
Self::new(
ErrorCore::new(
0,
TypeError,
loc,
switch_lang!(
"japanese" => format!("{name}の型が違います。\n予期した型: {GREEN}{expect}{RESET}\n与えられた型: {RED}{found}{RESET}"),
"english" => format!("the type of {name} is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"),
),
None,
),
caused_by,
)
}
pub fn return_type_error(
@ -386,10 +431,19 @@ impl TyCheckError {
expect: &Type,
found: &Type,
) -> Self {
Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!(
format!("the return type of {name} is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"),
format!("{name}の戻り値の型が違います。\n予期した型: {GREEN}{expect}{RESET}\n与えられた型: {RED}{found}{RESET}")
), None), caused_by)
Self::new(
ErrorCore::new(
0,
TypeError,
loc,
switch_lang!(
"japanese" => format!("{name}の戻り値の型が違います。\n予期した型: {GREEN}{expect}{RESET}\n与えられた型: {RED}{found}{RESET}"),
"english" => format!("the return type of {name} is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"),
),
None,
),
caused_by,
)
}
pub fn uninitialized_error(loc: Location, caused_by: Str, name: &str, t: &Type) -> Self {
@ -399,8 +453,8 @@ impl TyCheckError {
NameError,
loc,
switch_lang!(
format!("{name}: {t} is not initialized"),
format!("{name}: {t}は初期化されていません")
"japanese" => format!("{name}: {t}は初期化されていません"),
"english" => format!("{name}: {t} is not initialized"),
),
None,
),
@ -409,10 +463,19 @@ impl TyCheckError {
}
pub fn argument_error(loc: Location, caused_by: Str, expect: usize, found: usize) -> Self {
Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!(
format!("the number of positional arguments is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"),
format!("ポジショナル引数の数が違います。\n予期した個数: {GREEN}{expect}{RESET}\n与えられた個数: {RED}{found}{RESET}")
), None), caused_by)
Self::new(
ErrorCore::new(
0,
TypeError,
loc,
switch_lang!(
"japanese" => format!("ポジショナル引数の数が違います。\n予期した個数: {GREEN}{expect}{RESET}\n与えられた個数: {RED}{found}{RESET}"),
"english" => format!("the number of positional arguments is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"),
),
None,
),
caused_by,
)
}
pub fn match_error(loc: Location, caused_by: Str, expr_t: &Type) -> Self {
@ -422,8 +485,8 @@ impl TyCheckError {
TypeError,
loc,
switch_lang!(
format!("not all patterns of type {expr_t} are covered"),
format!("{expr_t}型の全パターンを網羅していません")
"japanese" => format!("{expr_t}型の全パターンを網羅していません"),
"english" => format!("not all patterns of type {expr_t} are covered"),
),
None,
),
@ -438,8 +501,8 @@ impl TyCheckError {
TypeError,
loc,
switch_lang!(
format!("failed to infer the type of {expr}"),
format!("{expr}の型が推論できません")
"japanese" => format!("{expr}の型が推論できません"),
"english" => format!("failed to infer the type of {expr}"),
),
None,
),
@ -463,8 +526,8 @@ impl TyCheckError {
AssignError,
loc,
switch_lang!(
format!("cannot assign twice to the immutable variable {name}"),
format!("定数{name}には再代入できません")
"japanese" => format!("定数{name}には再代入できません"),
"english" => format!("cannot assign twice to the immutable variable {name}"),
),
None,
),
@ -487,18 +550,18 @@ impl TyCheckError {
TypeError,
loc,
switch_lang!(
format!(
"japanese" => format!(
"{name}に渡された引数の数が多すぎます。
: {GREEN}{params_len}{RESET}
: {RED}{pos_args_len}{RESET}
: {RED}{kw_args_len}{RESET}"
),
"english" => format!(
"too many arguments for {name}:
total expected params: {GREEN}{params_len}{RESET}
passed positional args: {RED}{pos_args_len}{RESET}
passed keyword args: {RED}{kw_args_len}{RESET}"
),
format!(
"{name}に渡された引数の数が多すぎます。
: {GREEN}{params_len}{RESET}
: {RED}{pos_args_len}{RESET}
: {RED}{kw_args_len}{RESET}"
)
),
None,
),
@ -519,8 +582,8 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
TypeError,
loc,
switch_lang!(
format!("{name}'s argument {RED}{arg_name}{RESET} is passed multiple times"),
format!("{name}の引数{RED}{arg_name}{RESET}が複数回渡されています")
"japanese" => format!("{name}の引数{RED}{arg_name}{RESET}が複数回渡されています"),
"english" => format!("{name}'s argument {RED}{arg_name}{RESET} is passed multiple times"),
),
None,
),
@ -541,8 +604,8 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
TypeError,
loc,
switch_lang!(
format!("{name} got unexpected keyword argument {RED}{param_name}{RESET}"),
format!("{name}には予期しないキーワード引数{RED}{param_name}{RESET}が渡されています")
"japanese" => format!("{name}には予期しないキーワード引数{RED}{param_name}{RESET}が渡されています"),
"english" => format!("{name} got unexpected keyword argument {RED}{param_name}{RESET}"),
),
None,
),
@ -558,8 +621,8 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
UnusedWarning,
loc,
switch_lang!(
format!("{YELLOW}{name}{RESET} is not used"),
format!("{YELLOW}{name}{RESET}は使用されていません")
"japanese" => format!("{YELLOW}{name}{RESET}は使用されていません"),
"english" => format!("{YELLOW}{name}{RESET} is not used"),
),
None,
),
@ -580,10 +643,19 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
(None, Some(r)) => r,
(None, None) => Location::Unknown,
};
Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!(
format!("unification failed:\nlhs: {YELLOW}{lhs_t}{RESET}\nrhs: {YELLOW}{rhs_t}{RESET}"),
format!("型の単一化に失敗しました:\n左辺: {YELLOW}{lhs_t}{RESET}\n右辺: {YELLOW}{rhs_t}{RESET}")
), None), caused_by)
Self::new(
ErrorCore::new(
0,
TypeError,
loc,
switch_lang!(
"japanese" => format!("型の単一化に失敗しました:\n左辺: {YELLOW}{lhs_t}{RESET}\n右辺: {YELLOW}{rhs_t}{RESET}"),
"english" => format!("unification failed:\nlhs: {YELLOW}{lhs_t}{RESET}\nrhs: {YELLOW}{rhs_t}{RESET}"),
),
None,
),
caused_by,
)
}
pub fn re_unification_error(
@ -599,10 +671,19 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
(None, Some(r)) => r,
(None, None) => Location::Unknown,
};
Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!(
format!("re-unification failed:\nlhs: {YELLOW}{lhs_t}{RESET}\nrhs: {YELLOW}{rhs_t}{RESET}"),
format!("型の再単一化に失敗しました:\n左辺: {YELLOW}{lhs_t}{RESET}\n右辺: {YELLOW}{rhs_t}{RESET}")
), None), caused_by)
Self::new(
ErrorCore::new(
0,
TypeError,
loc,
switch_lang!(
"japanese" => format!("型の再単一化に失敗しました:\n左辺: {YELLOW}{lhs_t}{RESET}\n右辺: {YELLOW}{rhs_t}{RESET}"),
"english" => format!("re-unification failed:\nlhs: {YELLOW}{lhs_t}{RESET}\nrhs: {YELLOW}{rhs_t}{RESET}"),
),
None,
),
caused_by,
)
}
pub fn subtyping_error(
@ -618,17 +699,35 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
(None, Some(r)) => r,
(None, None) => Location::Unknown,
};
Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!(
format!("subtype constraints cannot be satisfied:\nsubtype: {YELLOW}{sub_t}{RESET}\nsupertype: {YELLOW}{sup_t}{RESET}"),
format!("部分型制約を満たせません:\nサブタイプ: {YELLOW}{sub_t}{RESET}\nスーパータイプ: {YELLOW}{sup_t}{RESET}")
), None), caused_by)
Self::new(
ErrorCore::new(
0,
TypeError,
loc,
switch_lang!(
"japanese" => format!("部分型制約を満たせません:\nサブタイプ: {YELLOW}{sub_t}{RESET}\nスーパータイプ: {YELLOW}{sup_t}{RESET}"),
"english" => format!("subtype constraints cannot be satisfied:\nsubtype: {YELLOW}{sub_t}{RESET}\nsupertype: {YELLOW}{sup_t}{RESET}"),
),
None,
),
caused_by,
)
}
pub fn pred_unification_error(lhs: &Predicate, rhs: &Predicate, caused_by: Str) -> Self {
Self::new(ErrorCore::new(0, TypeError, Location::Unknown, switch_lang!(
format!("predicate unification failed:\nlhs: {YELLOW}{lhs}{RESET}\nrhs: {YELLOW}{rhs}{RESET}"),
format!("述語式の単一化に失敗しました:\n左辺: {YELLOW}{lhs}{RESET}\n右辺: {YELLOW}{rhs}{RESET}")
), None), caused_by)
Self::new(
ErrorCore::new(
0,
TypeError,
Location::Unknown,
switch_lang!(
"japanese" => format!("述語式の単一化に失敗しました:\n左辺: {YELLOW}{lhs}{RESET}\n右辺: {YELLOW}{rhs}{RESET}"),
"english" => format!("predicate unification failed:\nlhs: {YELLOW}{lhs}{RESET}\nrhs: {YELLOW}{rhs}{RESET}"),
),
None,
),
caused_by,
)
}
pub fn has_effect<S: Into<Str>>(expr: &Expr, caused_by: S) -> Self {
@ -638,8 +737,8 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
HasEffect,
expr.loc(),
switch_lang!(
format!("this expression causes a side-effect"),
format!("この式には副作用があります")
"japanese" => format!("この式には副作用があります"),
"english" => format!("this expression causes a side-effect"),
),
None,
),
@ -659,14 +758,14 @@ passed keyword args: {RED}{kw_args_len}{RESET}"
MoveError,
name_loc,
switch_lang!(
format!(
"japanese" => format!(
"{RED}{name}{RESET}は{}行目ですでに移動されています",
moved_loc.ln_begin().unwrap()
),
"english" => format!(
"{RED}{name}{RESET} was moved in line {}",
moved_loc.ln_begin().unwrap()
),
format!(
"{RED}{name}{RESET}は{}行目ですでに移動されています",
moved_loc.ln_begin().unwrap()
)
),
None,
),

View file

@ -62,13 +62,13 @@ impl ASTLowerer {
expr.loc(),
self.ctx.name.clone(),
switch_lang!(
"the evaluation result of the expression is not used",
"式の評価結果が使われていません",
"japanese" => "式の評価結果が使われていません",
"english" => "the evaluation result of the expression is not used",
),
Some(
switch_lang!(
"if you don't use the value, use `discard` function",
"値を使わない場合は、discard関数を使用してください",
"japanese" => "値を使わない場合は、discard関数を使用してください",
"english" => "if you don't use the value, use `discard` function",
)
.into(),
),

View file

@ -12,6 +12,8 @@ homepage = "https://erg-lang.github.io/"
[features]
debug = [ "erg_common/debug" ]
japanese = [ "erg_common/japanese" ]
simplified_chinese = [ "erg_common/simplified_chinese" ]
traditional_chinese = [ "erg_common/traditional_chinese" ]
[dependencies]
erg_common = { version = "0.2.5", path = "../erg_common" }

View file

@ -21,10 +21,16 @@ impl LexError {
}
pub fn compiler_bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self {
Self::new(ErrorCore::new(errno, CompilerSystemError, loc, switch_lang!(
format!("this is a bug of the Erg compiler, please report it to https://github.com/mtshiba/erg\ncaused from: {fn_name}:{line}"),
format!("これはErg compilerのバグです、開発者に報告して下さい (https://github.com/mtshiba/erg)\n{fn_name}:{line}より発生")
), None))
Self::new(ErrorCore::new(
errno,
CompilerSystemError,
loc,
switch_lang!(
"japanese" => format!("これはErg compilerのバグです、開発者に報告して下さい (https://github.com/mtshiba/erg)\n{fn_name}:{line}より発生"),
"english" => format!("this is a bug of the Erg compiler, please report it to https://github.com/mtshiba/erg\ncaused from: {fn_name}:{line}"),
),
None,
))
}
pub fn feature_error(errno: usize, loc: Location, name: &str) -> Self {
@ -33,8 +39,9 @@ impl LexError {
FeatureError,
loc,
switch_lang!(
format!("this feature({name}) is not implemented yet"),
format!("この機能({name})はまだ正式に提供されていません")
"japanese" => format!("この機能({name})はまだ正式に提供されていません"),
"simplified_chinese" => format!("该功能({name})还没有正式提供"),
"english" => format!("this feature({name}) is not implemented yet"),
),
None,
))
@ -45,7 +52,11 @@ impl LexError {
errno,
SyntaxError,
loc,
switch_lang!("invalid syntax", "不正な構文です"),
switch_lang!(
"japanese" => "不正な構文です",
"simplified_chinese" => "无效的语法",
"english" => "invalid syntax",
),
None,
))
}

View file

@ -266,8 +266,8 @@ impl Lexer /*<'a>*/ {
0,
comment.loc(),
switch_lang!(
"invalid unicode character (bi-directional override) in comments",
"不正なユニコード文字(双方向オーバーライド)がコメント中に使用されています"
"japanese" => "不正なユニコード文字(双方向オーバーライド)がコメント中に使用されています",
"english" => "invalid unicode character (bi-directional override) in comments",
),
None,
));
@ -298,7 +298,10 @@ impl Lexer /*<'a>*/ {
Some(Err(LexError::syntax_error(
0,
space.loc(),
switch_lang!("invalid indent", "インデントが不正です"),
switch_lang!(
"japanese" => "インデントが不正です",
"english" => "invalid indent",
),
None,
)))
} else if self.prev_token.is(Newline) {
@ -317,11 +320,14 @@ impl Lexer /*<'a>*/ {
return Some(Err(LexError::syntax_error(
0,
token.loc(),
switch_lang!("indentation is too deep", "インデントが深すぎます"),
switch_lang!(
"japanese" => "インデントが深すぎます",
"english" => "indentation is too deep",
),
Some(
switch_lang!(
"The code is too complicated. Please split the process.",
"コードが複雑すぎます。処理を分割してください"
"japanese" => "コードが複雑すぎます。処理を分割してください",
"english" => "The code is too complicated. Please split the process",
)
.into(),
),
@ -359,7 +365,10 @@ impl Lexer /*<'a>*/ {
Some(Err(LexError::syntax_error(
0,
invalid_dedent.loc(),
switch_lang!("invalid indent", "インデントが不正です"),
switch_lang!(
"japanese" => "インデントが不正です",
"english" => "invalid indent",
),
None,
)))
}
@ -529,8 +538,8 @@ impl Lexer /*<'a>*/ {
0,
token.loc(),
switch_lang!(
"invalid unicode character (bi-directional override) in string literal",
"不正なユニコード文字(双方向オーバーライド)が文字列中に使用されています"
"japanese" => "不正なユニコード文字(双方向オーバーライド)が文字列中に使用されています",
"english" => "invalid unicode character (bi-directional override) in string literal",
),
None,
));
@ -542,8 +551,8 @@ impl Lexer /*<'a>*/ {
0,
token.loc(),
switch_lang!(
"the string is not closed by \"",
"文字列が\"によって閉じられていません"
"japanese" => "文字列が\"によって閉じられていません",
"english" => "the string is not closed by \"",
),
None,
))
@ -590,7 +599,10 @@ impl Iterator for Lexer /*<'a>*/ {
Some(Err(LexError::syntax_error(
0,
token.loc(),
switch_lang!("no such operator: <.", "<.という演算子はありません"),
switch_lang!(
"japanese" => "<.という演算子はありません",
"english" => "no such operator: <.",
),
None,
)))
}
@ -764,8 +776,17 @@ impl Iterator for Lexer /*<'a>*/ {
Some(Err(LexError::syntax_error(
0,
token.loc(),
switch_lang!("cannot use a tab as a space", "タブ文字は使用できません"),
Some(switch_lang!("use spaces", "スペースを使用してください").into()),
switch_lang!(
"japanese" => "タブ文字は使用できません",
"english" => "cannot use a tab as a space",
),
Some(
switch_lang!(
"japanese" => "スペース( )を使用してください",
"english" => "use spaces ( )",
)
.into(),
),
)))
}
// TODO:
@ -788,8 +809,8 @@ impl Iterator for Lexer /*<'a>*/ {
0,
token.loc(),
switch_lang!(
format!("`{}` cannot be defined by user", &token.content),
format!("`{}`はユーザー定義できません", &token.content)
"japanese" => format!("`{}`はユーザー定義できません", &token.content),
"english" => format!("`{}` cannot be defined by user", &token.content),
),
None,
)));
@ -802,8 +823,8 @@ impl Iterator for Lexer /*<'a>*/ {
0,
token.loc(),
switch_lang!(
format!("back quotes (`) not closed"),
format!("バッククォート(`)が閉じられていません")
"japanese" => format!("バッククォート(`)が閉じられていません"),
"english" => format!("back quotes (`) not closed"),
),
None,
)))
@ -819,8 +840,8 @@ impl Iterator for Lexer /*<'a>*/ {
0,
token.loc(),
switch_lang!(
format!("invalid character: '{invalid}'"),
format!("この文字は使用できません: '{invalid}'")
"japanese" => format!("この文字は使用できません: '{invalid}'"),
"english" => format!("invalid character: '{invalid}'"),
),
None,
)))

View file

@ -439,7 +439,10 @@ impl Parser {
let err = ParseError::syntax_error(
0,
loc,
switch_lang!("failed to parse a block", "ブロックの解析に失敗しました"),
switch_lang!(
"japanese" => "ブロックの解析に失敗しました",
"english" => "failed to parse a block",
),
None,
);
Err(err)
@ -496,8 +499,8 @@ impl Parser {
0,
self.peek().unwrap().loc(),
switch_lang!(
"Cannot use type bounds in a declaration of a variable",
"変数宣言で型制約は使えません"
"japanese" => "変数宣言で型制約は使えません",
"english" => "Cannot use type bounds in a declaration of a variable",
),
None,
);
@ -509,16 +512,6 @@ impl Parser {
self.skip();
Some(self.try_reduce_type_spec()?)
} else {
if self.cur_is(Colon) {
self.warns.push(ParseError::syntax_warning(0, name.loc(), switch_lang!(
"Since it is obvious that the variable is of type `Type`, there is no need to specify the type.",
"変数がType型であることは明らかなので、型指定は不要です。"
), Some(switch_lang!(
"Are you sure you're not confusing it with subclass declaration (<:)?",
"サブクラスの宣言(<:)ではありませんか?"
).into())
));
}
None
}
} else if self.cur_is(Colon) {
@ -663,8 +656,8 @@ impl Parser {
0,
t.loc(),
switch_lang!(
"Binary operators cannot be used in left-values",
"左辺値の中で中置演算子は使えません"
"japanese" => "左辺値の中で中置演算子は使えません",
"english" => "Binary operators cannot be used in left-values",
),
None,
);
@ -754,9 +747,10 @@ impl Parser {
return Err(ParseError::syntax_error(
0,
param.loc(),
// TODO: switch_lang!
"non-default argument follows default argument",
None,
))
));
}
(false, false) => {
non_default_params.push(param);
@ -768,8 +762,8 @@ impl Parser {
0,
t.loc(),
switch_lang!(
"Binary operators cannot be used in parameters",
"仮引数の中で中置演算子は使えません"
"japanese" => "仮引数の中で中置演算子は使えません",
"english" => "Binary operators cannot be used in parameters",
),
None,
);
@ -1021,12 +1015,15 @@ impl Parser {
Ok(ConstExpr::Accessor(ConstAccessor::Local(local)))
}
// TODO: App, Array, Record, BinOp, UnaryOp,
other => {
Err(ParseError::syntax_error(0, other.loc(), switch_lang!(
"this expression is not computable at the compile-time, so cannot used as a type-argument",
"この式はコンパイル時計算できないため、型引数には使用できません",
), None))
}
other => Err(ParseError::syntax_error(
0,
other.loc(),
switch_lang!(
"japanese" => "この式はコンパイル時計算できないため、型引数には使用できません",
"english" => "this expression is not computable at the compile-time, so cannot used as a type-argument",
),
None,
)),
}
}

View file

@ -0,0 +1,59 @@
# Multilingualization of Messages
Erg is working on making all messages (start, option, doc, hint, warning, error messages, etc.) multilingual within the language.
This project is open to anyone without detailed knowledge of Rust or Erg. Your participation is always welcome.
Here is how to translate them.
## Search `switch_lang!`
In the Erg source code, look for the item `switch_lang!` (use grep or your editor's search function).
You should find something like this:
```rust
switch_lang!(
"japanese" => format!("この機能({name})はまだ正式に提供されていません"),
"english" => format!("this feature({name}) is not implemented yet"),
),
```
This message is currently supported only in Japanese and English. Let's add a simplified Chinese message as a test.
## Add a New Message
Add translated messages as you see the content in other languages. Don't forget the comma (`,`) last.
```rust
switch_lang!(
"japanese" => format!("この機能({name})はまだ正式に提供されていません"),
"simplified_chinese" => format!("该功能({name})还没有正式提供"),
"english" => format!("this feature({name}) is not implemented yet"),
),
```
Note that English is the default and must come last.
The `{name}` part is a Rust formatting feature that allows you to embed the contents of a variable (`name`) into a string.
## Build
Now, let's build with the `--features simplified_chinese` option.
<img src="../../../assets/screenshot_i18n_messages.png" alt='screenshot_i18n_messages'>
We did it!
## FAQ
Q: What does a specification like `{RED}{foo}{RESET}` mean?
A: {RED} and subsequent letters will be displayed in red. {RESET} will restore the color.
Q: If I want to add my language, how do I replace the `"simplified_chinese" =>` part?
The following languages are currently supported:
* "english" (default)
* "japanese"
* "simplified_chinese"
* "traditional_chinese"
If you would like to add languages other than these, please make a request.

View file

@ -0,0 +1,58 @@
# Multilingualization of Messages
Ergはメッセージ(スタート、オプション、ドキュメント、ヒント、警告、エラーメッセージなど)の多言語化を進めています。
このプロジェクトは、RustやErgの詳しい知識がなくても参加することができます。ぜひ協力をお願いします。
以下に、多言語化の方法を説明します。
## `switch_lang!`を探す
Ergのソースコードの中で、`switch_lang!`という項目を探します(grepやエディタの検索機能を使ってください)。
以下のようなものが見つかるはずです。
```rust
switch_lang!(
"japanese" => format!("この機能({name})はまだ正式に提供されていません"),
"english" => format!("this feature({name}) is not implemented yet"),
),
```
このメッセージは現在、日本語と英語のみでサポートされています。試しに簡体字のメッセージを追加してみましょう。
## メッセージを追加する
他の言語の内容を見ながら、翻訳されたメッセージを追加してください。最後にカンマ(`,`)を忘れないでください。
```rust
switch_lang!(
"japanese" => format!("この機能({name})はまだ正式に提供されていません"),
"simplified_chinese" => format!("该功能({name})还没有正式提供"),
"english" => format!("this feature({name}) is not implemented yet"),
),
```
なお、英語はデフォルトであり、必ず最後に来るようにします。
`{name}` の部分は Rust のフォーマット機能で、変数の内容 (`name`) を文字列に埋め込むことができます。
## Build
では、`--features simplified_chinese` オプションを付けてビルドしてみましょう。
<img src="../../../assets/screenshot_i18n_messages.png" alt='screenshot_i18n_messages'>
やりましたね!
## FAQ
Q: `{RED}{foo}{RESET}` のような指定は何を意味するのでしょうか?
A: {RED}以降が赤色で表示されます。{RESET}で色を元に戻します。
Q: 自分の言語を追加したい場合、`"simplified_chinese" =>`の部分はどのように置き換えればよいですか?
A: 現在、以下の言語がサポートされています。
* "english" (デフォルト)
* "japanese" (日本語)
* "simplified_chinese" (簡体字中国語)
* "traditional_chinese"(繁体字中国語)
これら以外の言語を追加したい場合は、リクエストしてください。